/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-c++-*-
*****************************************************************************
*
* file:         udrload.cpp
* description:  this is the module that processes udr load messages.
*               the tasks for this process are to :
*               . extract sp descriptive attributes from message
*               . extract sp parameter attributes from message
*               . build SPInfo data structure and populate with attributes
*               . return sp id (creation timestamp) to client
*               . handle errors returned from lm.
*               . deal with resource allocation problems of SPInfo data structures
*
* created:      01/01/2001
* language:     c++
*
*
*****************************************************************************
*/

#include "udrextrn.h"
#include "spinfo.h"
#include "udrdefs.h"
#include "udrutil.h"
#include "ComDiags.h"
#include "ComSmallDefs.h"
#include "UdrStreams.h"
#include "UdrExeIpc.h"
#include "LmRoutineC.h"
#include "LmRoutineCppObj.h"
#include "LmRoutineJavaObj.h"
#include "LmJavaOptions.h"
#include "udrdecs.h"
#include "spinfoCallback.h"
// include the file generated by this command and checked into git:
// javah -d $TRAF_HOME/../sql/langman org.trafodion.sql.udr.UDR 
#include "org_trafodion_sql_udr_UDR.h"

class UdrServerReplyStream;

SPInfo * processLoadParameters(UdrGlobals *UdrGlob,
                               UdrLoadMsg &request,
                               ComDiagsArea &d);
void displayLoadParameters(UdrLoadMsg &request);
void reportLoadResults(UdrGlobals *UdrGlob, SPInfo *sp, LmRoutine *lmr_);
void copyRoutineOptionalData(UdrLoadMsg &request, SPInfo *sp);

void processALoadMessage(UdrGlobals *UdrGlob,
                         UdrServerReplyStream &msgStream,
                         UdrLoadMsg &request,
                         IpcEnvironment &env)
{
  const char *moduleName = "processALoadMessage";
  char errorText[MAXERRTEXT];

  ComDiagsArea *diags = ComDiagsArea::allocate(UdrGlob->getIpcHeap());

  doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
               UdrGlob->showLoad_, moduleName);

  NABoolean showLoadLogic = (UdrGlob->verbose_ &&
                             UdrGlob->traceLevel_ >= TRACE_IPMS &&
                             UdrGlob->showLoad_);

  if (showLoadLogic)
  {
    ServerDebug("[UdrServ (%s)] Processing load request", moduleName);
  }

  // UDR_LOAD message always comes with transaction and they are out
  // side Enter Tx and Exit Tx pair. Make sure we are under correct
  // transaction.
  msgStream.activateCurrentMsgTransaction();

  //
  // Check to see if the incoming UDR handle has already been seen
  //
  NABoolean handleAlreadyExists = FALSE;
  SPInfo *sp = UdrGlob->getSPList()->spFind(request.getHandle());
  if (sp)
  {
    handleAlreadyExists = TRUE;
    if (showLoadLogic)
    {
      ServerDebug("    Duplicate handle arrived");
      ServerDebug("    SPInfoState is %s", sp->getSPInfoStateString());
    }

    if (sp->getSPInfoState() != SPInfo::UNLOADING)
    {
      //
      // An SPInfo exists but it is not one of the token instances to
      // represent an out-of-sequence LOAD/UNLOAD. This is an internal
      // error. Something has been botched in the message protocol.
      //
      char buf[100];
      convertInt64ToAscii(request.getHandle(), buf);
      *diags << DgSqlCode(-UDR_ERR_DUPLICATE_LOADS);
      *diags << DgString0(buf);
    }
    else
    {
      // The LOAD/UNLOAD requests for this handle arrived
      // out-of-sequence. Nothing to do at this point. An empty reply
      // will be generated later in this function.
    }
  }

  if (!handleAlreadyExists)
  {
    if (!UdrHandleIsValid(request.getHandle()))
    {
      *diags << DgSqlCode(-UDR_ERR_MISSING_UDRHANDLE);
      *diags << DgString0("Load Message");
    }
    else
    {
      //
      // First process the metadata in the LOAD requests and then
      // contact Language Manager to load the SP.
      //
      sp = processLoadParameters(UdrGlob, request, *diags);
      
      if (showLoadLogic)
      {
        ServerDebug("[UdrServ (%s)]  About to call LM::getRoutine",
                    moduleName);
      }
      
      if (sp == NULL)
      {
        *diags << DgSqlCode(-UDR_ERR_UNABLE_TO_ALLOCATE_MEMORY);
        *diags << DgString0("SPInfo");
      }
      else
      {
        UdrGlob->setCurrSP(sp);
        LmRoutine *lmRoutine;
        LmResult lmResult;
        LmLanguageManager *lm =
          UdrGlob->getOrCreateLM(lmResult, sp->getLanguage(), diags);
        LmHandle emitRowFuncPtr;

        if (sp->getParamStyle() == COM_STYLE_CPP_OBJ)
          emitRowFuncPtr = (LmHandle)&SpInfoEmitRowCpp;
        else
          emitRowFuncPtr = (LmHandle)&SpInfoEmitRow;
        
        if (lm)
        {
          if (sp->getParamStyle() == COM_STYLE_JAVA_OBJ ||
              sp->getParamStyle() == COM_STYLE_CPP_OBJ)
            {
              lmResult = lm->getObjRoutine(
                   request.getUDRSerInvocationInfo(),
                   request.getUDRSerInvocationInfoLen(),
                   request.getUDRSerPlanInfo(),
                   request.getUDRSerPlanInfoLen(),
                   sp->getLanguage(),
                   sp->getParamStyle(),
                   sp->getExternalName(),
                   sp->getContainerName(),
                   sp->getExternalPathName(),
                   sp->getLibrarySqlName(),
                   &lmRoutine,
                   diags);

              if (lmRoutine && lmResult == LM_OK)
                {
                  if (sp->getParamStyle() == COM_STYLE_CPP_OBJ)
                    {
                      LmRoutineCppObj *cppRoutine =
                        static_cast<LmRoutineCppObj *>(lmRoutine);

#ifndef NDEBUG
                      int debugLoop = 2;

                      if (cppRoutine->getInvocationInfo()->getDebugFlags() &
                          tmudr::UDRInvocationInfo::DEBUG_LOAD_MSG_LOOP)
                        debugLoop = 1;
                      // go into a loop to allow the user to attach a debugger,
                      // if requested, set debugLoop = 2 in the debugger to get out
                      while (debugLoop < 2)
                        debugLoop = 1-debugLoop;
#endif

                    }
                  else if (sp->getParamStyle() == COM_STYLE_JAVA_OBJ)
                    {
                      LmRoutineJavaObj *javaObjRoutine =
                        static_cast<LmRoutineJavaObj *>(lmRoutine);

                      // avoid unpacking the UDRInvocationInfo to look
                      // up row lengths and take those from the request
                      // message instead
                      javaObjRoutine->setRowLengths(
                           request.getInputRowSize(),
                           request.getOutputRowSize());

                      // provide pointers of the JNI functions with special
                      // parameter lists
                      javaObjRoutine->setJNIFunctionPtrs(
                           reinterpret_cast<void *>(&Java_org_trafodion_sql_udr_UDR_SpInfoGetNextRowJava),
                           reinterpret_cast<void *>(&Java_org_trafodion_sql_udr_UDR_SpInfoEmitRowJava));
                    }

                  // set function pointers for functions provided
                  // by tdm_udrserv
                  lmResult = lmRoutine->setFunctionPtrs(SpInfoGetNextRow,
                                                        SpInfoEmitRowCpp,
                                                        diags);

                  if (lmResult == LM_OK)
                    // add items to the UDRInvocationInfo that are not
                    // known at compile time (total # of instances is
                    // kind of known, but we want to give the executor a
                    // chance to change it)
                    lmResult = lmRoutine->setRuntimeInfo(
                         request.getParentQid(),
                         request.getNumInstances(),
                         request.getInstanceNum(),
                         diags);
                }
            }
          else
            lmResult = lm->getRoutine(sp->getNumParameters(),
                                      sp->getLmParameters(),
                                      sp->getNumTables(),
                                      sp->getLmTables(),
                                      sp->getReturnValue(),
                                      sp->getParamStyle(),
                                      sp->getTransactionAttrs(),
                                      sp->getSQLAccessMode(),
                                      sp->getParentQid(),
                                      sp->getRequestRowSize(),
                                      sp->getReplyRowSize(),
                                      sp->getSqlName(),
                                      sp->getExternalName(),
                                      sp->getRoutineSig(),
                                      sp->getContainerName(),
                                      sp->getExternalPathName(),
                                      sp->getLibrarySqlName(),
                                      UdrGlob->getCurrentUserName(),
                                      UdrGlob->getSessionUserName(),
                                      sp->getExternalSecurity(),
                                      sp->getRoutineOwnerId(),
                                      &lmRoutine,
                                      (LmHandle)&SpInfoGetNextRow,
                                      emitRowFuncPtr,
                                      sp->getMaxNumResultSets(),
                                      diags);
        }
        
        if (lmResult == LM_OK)
        {
          if (lmRoutine == NULL)
          {
            *diags << DgSqlCode(-UDR_ERR_MISSING_LMROUTINE);
            *diags << DgString0("error: returned a null LM handle");
            *diags << DgInt1((Int32)0);
          }
          else
          {
            sp->setLMHandle(lmRoutine);

	    // Retrieve any optional data from UdrLoadMsg.
	    copyRoutineOptionalData(request, sp);

            reportLoadResults(UdrGlob, sp, lmRoutine);
            sp->setSPInfoState(SPInfo::LOADED);
          }

        } // lmResult == LM_OK

        if (showLoadLogic)
        {
          if (lmResult == LM_OK)
          {
            sprintf(errorText,
                    "[UdrServ (%.30s)]  LM::getRoutine was successful.",
                    moduleName);
          }
          else
          {
            sprintf(errorText,
                    "[UdrServ (%.30s)]  LM::getRoutine resulted in error.",
                    moduleName);
          }
          ServerDebug(errorText);
          doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
                       UdrGlob->showMain_, errorText);
        }

        if (sp && !(sp->isLoaded()))
        {
          sp->setSPInfoState(SPInfo::LOAD_FAILED);
        }

      } // if (sp == NULL) else ...
    } // if (handle is not valid) else ...
  } // !handleAlreadyExists

  // build a reply and send it
  msgStream.clearAllObjects();

  UdrLoadReply *reply = new (UdrGlob->getIpcHeap())
    UdrLoadReply(UdrGlob->getIpcHeap());

  if (reply == NULL)
  {  // no reply buffer build...
    controlErrorReply(UdrGlob, msgStream, UDR_ERR_MESSAGE_PROCESSING,
                      INVOKE_ERR_NO_REPLY_BUFFER, NULL);
    return;
  }

  // Only return a valid UDR Handle if diagnostics are not present and
  // no LM errors occurred. We also return a valid handle if this LOAD
  // arrived out-of-sequence after the UNLOAD and no diagnostics have
  // been generated yet.
  if (diags && diags->getNumber() > 0)
  {
    reply->setHandle(INVALID_UDR_HANDLE);
  }
  else if (sp)
  {
    if (sp->isLoaded() || handleAlreadyExists)
    {
      reply->setHandle(sp->getUdrHandle());
    }
    else
    {
      reply->setHandle(INVALID_UDR_HANDLE);
    }
  }

  msgStream << *reply;

  if (diags && diags->getNumber() > 0)
  {
    msgStream << *diags;
    UdrGlob->numErrUDR_++;
    UdrGlob->numErrSP_++;
    UdrGlob->numErrLoadSP_++;
    if (showLoadLogic)
      dumpDiagnostics(diags, 2);
  }

  if (showLoadLogic)
  {
    ServerDebug("[UdrServ (%s)] About to send LOAD reply", moduleName);
  }

#ifdef _DEBUG
  if (UdrGlob && UdrGlob->getJavaLM())
  {
    sleepIfPropertySet(*(UdrGlob->getJavaLM()),
                       "MXUDR_LOAD_DELAY", diags);
  }
#endif // _DEBUG

  sendControlReply(UdrGlob, msgStream, sp);

  if (diags)
  {
    diags->decrRefCount();
  }

  reply->decrRefCount();

} // processALoadMessage

SPInfo *processLoadParameters(UdrGlobals *UdrGlob,
                              UdrLoadMsg &request,
                              ComDiagsArea &d)
{
  const char *moduleName = "processLoadParameters";
  Lng32 oldDiags = 0;
  Lng32 newDiags = 0;
  
  doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
               UdrGlob->showLoad_, moduleName);
  // check for test mode processing...
  
  SPInfo *sp = NULL;
  int numRetries = 0;
  
  // process message parameters...
  
  if (UdrGlob->traceLevel_ >= TRACE_DETAILS &&
      UdrGlob->showLoad_)
  {
    displayLoadParameters(request);
  }
  
  doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
               UdrGlob->showLoad_, "create SPInfo");
  
  oldDiags = d.getNumber();

  while (sp == NULL && numRetries < 1)
    {
      sp = new (UdrGlob->getUdrHeap()) SPInfo(UdrGlob, UdrGlob->getUdrHeap(),
                                          request.getHandle(),
                                          (char *)request.getSqlName(),
                                          (char *)request.getRoutineName(),
                                          (char *)request.getSignature(),
                                          (char *)request.getContainerName(),
                                          (char *)request.getExternalPath(),
                                          (char *)request.getLibrarySqlName(),
                                          request.getNumParameters(),
                                          request.getNumInValues(),
                                          request.getNumOutValues(),
                                          request.getMaxResultSets(),
                                          request.getTransactionAttrs(),
                                          request.getSqlAccessMode(),
                                          request.getLanguage(),
                                          request.getParamStyle(),
                                          request.isIsolate(),
                                          request.isCallOnNull(),
                                          request.isExtraCall(),
                                          request.isDeterministic(),
                                          request.getExternalSecurity(),
                                          request.getRoutineOwnerId(),
                                          request.getInBufferSize(),
                                          request.getOutBufferSize(),
                                          request.getInputRowSize(),
                                          request.getOutputRowSize(),
                                          /* ComDiagsArea */ d,
                                          (char *)request.getParentQid());
      if (sp == NULL)
        {  // memory problem
          UdrGlob->getSPList()->releaseOldestSPJ(/* ComDiagsArea */ d);
          numRetries++;
        }
    }

  if (sp == NULL)
    {
      return NULL;
    }
  
  newDiags = d.getNumber();
  
  if (oldDiags != newDiags)
  {
    // diagnostics generated in ctor for SPInfo
    // something bad has happened. Bail out.
    delete sp;
    return NULL;
  }
  
  // Process IN/INOUT parameters
  ComUInt32 i;
  for (i = 0; i < sp->getNumInParameters(); i++)
  {
    UdrParameterInfo &pi = (UdrParameterInfo &)request.getInParam(i);

    // Copy information from the UdrParameterInfo into the spinfo. The
    // spinfo will initialize an LmParameter instance for this
    // parameter.
    sp->setInParam(i, pi);
  }
  
  // Process OUT/INOUT parameters
  for (ComUInt32 j = 0; j < sp->getNumOutParameters(); j++)
  {
    UdrParameterInfo &po = (UdrParameterInfo &)request.getOutParam(j);

    // Copy information from the UdrParameterInfo into the spinfo. The
    // spinfo will initialize an LmParameter instance for this
    // parameter. Note for INOUT parameters, the LmParameter will not
    // be completely re-initialized. It was partially initialized in
    // the loop above for input parameters. This setOutParam call
    // completes the LmParameter initialization.
    sp->setOutParam(j, po);
  }

  // Process table input info if any
  if(sp->getParamStyle() == COM_STYLE_SQLROW_TM ||
     sp->getParamStyle() == COM_STYLE_CPP_OBJ ||
     sp->getParamStyle() == COM_STYLE_JAVA_OBJ)
  {
    sp->setNumTableInfo(request.getNumInputTables());
    sp->setTableInputInfo(request.getInputTables()); 
  }

  if (request.getUdrJavaDebugPort() > 0 &&
      sp->getLanguage() == COM_LANGUAGE_JAVA &&
      UdrGlob->getJavaLM() == NULL) // Langman not started already
  {
    // Add debug options to the UDR globals. Note that this will
    // only work for the first UDR in this tmd_udrserv process,
    // since the options are evaluated when the JVM gets started.
    // Also note that the compiler will only generate these flags
    // for debug builds or for the DB_ROOT user (e.g. sqlci).
    char buf[200];

    int debugPort = request.getUdrJavaDebugPort();
    int timeout = request.getUdrJavaDebugTimeout();

    // if the debug port is a multiple of 1000 then add pid mod 1000
    // to the debug port, to be able to debug parallel queries with
    // multiple UDR servers
    if (debugPort % 1000 == 0)
      debugPort += (int) getpid() % 1000;

    if (timeout > 0)
      {
        // suspend the JVM and wait for <timeout> milliseconds for the
        // debugger to attach
        snprintf(
             buf,
             sizeof(buf),
             "-agentlib:jdwp=transport=dt_socket,address=%d,server=y,timeout=%d",
             debugPort,
             timeout);
        printf("Debugging tdm_udrserv JVM instance %d of %d, attach debugger to port %d within %d seconds\n",
               request.getInstanceNum(),
               request.getNumInstances(),
               debugPort,
               timeout/1000);
      }
    else
      {
        // don't suspend the JVM, attaching the debugger is optional
        snprintf(
             buf,
             sizeof(buf),
             "-agentlib:jdwp=transport=dt_socket,address=%d,server=y,suspend=n",
             debugPort);
        printf("Debugging tdm_udrserv JVM instance %d of %d, attach debugger to port %d\n",
               request.getInstanceNum(),
               request.getNumInstances(),
               debugPort);
      }

    UdrGlob->getJavaOptions()->addOption(buf, false);
  }

  if (UdrGlob->verbose_ &&
      UdrGlob->traceLevel_ >= TRACE_DETAILS &&
      UdrGlob->showLoad_)
  {
    sp->displaySPInfo(2);
  }
  
  if (UdrGlob->verbose_ &&
      UdrGlob->traceLevel_ >= TRACE_DATA_AREAS &&
      UdrGlob->showLoad_)
  {
    ServerDebug("  LmParameter array:");
    for (ComUInt32 i = 0; i < sp->getNumParameters(); i++)
    {
      dumpLmParameter(sp->getLmParameter(i), i, "    ");
    }
  }

  if (UdrGlob->verbose_ &&
      UdrGlob->traceLevel_ >= TRACE_DATA_AREAS &&
      UdrGlob->showLoad_)
    ServerDebug("");
  
  return sp;
} // processLoadParameters

void displayLoadParameters(UdrLoadMsg &request)
{
  ServerDebug("");
  ServerDebug("UDR load parameters:");
  ServerDebug("  UDR handle          : " INT64_SPEC , request.getHandle());
  ServerDebug("  sql name            : %s", request.getSqlName());
  ServerDebug("  routine name        : %s", request.getRoutineName());
  ServerDebug("  routine signature   : %s", request.getSignature());
  ServerDebug("  container name      : %s", request.getContainerName());
  ServerDebug("  external path name  : %s", request.getExternalPath());
  ServerDebug("  library SQL name    : %s", request.getLibrarySqlName());
  ServerDebug("  parent QID          : %s", request.getParentQid());
  
  const char *transString;
  switch (request.getTransactionAttrs() )
  {
    case COM_NO_TRANSACTION_REQUIRED:
      transString = "NO TRANSACTION REQUIRED";
      break;
    case COM_TRANSACTION_REQUIRED:
      transString = "TRANSACTION REQUIRED";
      break;    
    default:
      transString = "*** UNKNOWN ***";
      break;
  }
  ServerDebug("  transaction required     : %s", transString);

  const char *modeString;
  switch (request.getSqlAccessMode() )
  {
    case COM_NO_SQL:
      modeString = "NO SQL";
      break;
    case COM_CONTAINS_SQL:
      modeString = "CONTAINS SQL";
      break;
    case COM_READS_SQL:
      modeString = "READS SQL DATA";
      break;
    case COM_MODIFIES_SQL:
      modeString = "MODIFIES SQL DATA";
      break;
    default:
      modeString = "*** UNKNOWN ***";
      break;
  }
  ServerDebug("  sql access mode     : %s", modeString);

  const char *language;
  switch (request.getLanguage())
  {
    case COM_LANGUAGE_JAVA:
      language = "Java";
      break;

    case COM_LANGUAGE_C:
      language = "C";
      break;

    case COM_LANGUAGE_CPP:
      language = "C++";
      break;

    case COM_LANGUAGE_SQL:
      language = "SQL";
      break;

    default:
      language = "*** UNKNOWN ***";
      break;
  }
  ServerDebug("  language            : %s", language);

  const char *externalSecurity;
  switch (request.getExternalSecurity())
  {
    case COM_ROUTINE_EXTERNAL_SECURITY_INVOKER:
      externalSecurity = "EXTERNAL SECURITY INVOKER";
      break;

    case COM_ROUTINE_EXTERNAL_SECURITY_DEFINER:
      externalSecurity = "EXTERNAL SECURITY DEFINER";
      break;

    default:
      externalSecurity = "*** UNKNOWN ***";
      break;
  }
  ServerDebug("  externalSecurity    : %s", externalSecurity);
  ServerDebug("  routine Owner Id    : %d",
              (Int32) request.getRoutineOwnerId());

  ServerDebug("  max result sets     : %d",
              (Int32) request.getMaxResultSets());
  ServerDebug("  num params          : %d",
              (Int32) request.getNumParameters());
  ServerDebug("  num in values       : %d",
              (Int32) request.getNumInValues());
  ServerDebug("  num out values      : %d",
              (Int32) request.getNumOutValues());
  ServerDebug("  udr flags           : %d",
              (Int32) request.getUdrFlags());

  if (request.isIsolate())
    ServerDebug("    isolate           : TRUE");
  else
    ServerDebug("    isolate           : FALSE");

  if (request.isCallOnNull())
    ServerDebug("    call on NULL      : TRUE");
  else
    ServerDebug("    call on NULL      : FALSE");

  if (request.isExtraCall())
    ServerDebug("    extra call        : TRUE");
  else
    ServerDebug("    extra call        : FALSE");

  if (request.isDeterministic())
    ServerDebug("    deterministic     : TRUE");
  else
    ServerDebug("    deterministic     : FALSE");

  ServerDebug("");
  
} // displayLoadParameters

void reportLoadResults(UdrGlobals *UdrGlob, SPInfo *sp, LmRoutine *lmr_)
{
  const char *moduleName = "reportLoadResults";
  
  doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
               UdrGlob->showLoad_, moduleName);
  
  if (UdrGlob->verbose_ &&
      UdrGlob->traceLevel_ >= TRACE_DETAILS &&
      UdrGlob->showLoad_ )
  {
    ServerDebug("");
    ServerDebug("[UdrServ (%s)] LOAD message results:", moduleName);
    ServerDebug("  LOAD Udr Handle     : " INT64_SPEC ,
                (Int64) sp->getUdrHandle());
    ServerDebug("  LM result parameter : %d", (Int32) sp->getNumParameters());
    if (sp->getReturnValue() != NULL)
    {
      dumpLmParameter(*sp->getReturnValue(), 0, "    ");
    }
    if (lmr_)
    {
      ServerDebug("  LM routine          : ");
      dumpBuffer((unsigned char *)lmr_, sizeof(LmRoutine));
    }
    ServerDebug("");
  }

} // reportLoadResults

// Utility function to push optional data buffers from UdrLoadMsg
// into LM.
void copyRoutineOptionalData(UdrLoadMsg &request, SPInfo *sp)
{
  if (sp->getLanguage() != COM_LANGUAGE_C)
    return;
  
  ComUInt32 numBufs = request.getNumOptionalDataBufs();
  LmRoutineC *routine = (LmRoutineC *) sp->getLMHandle();
  routine->setNumPassThroughInputs(numBufs);
  
  for (ComUInt32 i = 0; i < numBufs; i++)
  {
    // Process each optional data buffer. Each buffer is a 4-byte
    // length field followed by data.
    ComUInt32 buflen = 0;
    char *dataBuf = request.getOptionalDataBuf(i);
    memcpy(&buflen, dataBuf, 4);
    if (buflen > 0)
      routine->setPassThroughInput(i, dataBuf + 4, buflen);
    else
      routine->setPassThroughInput(i, (void *)"", 0);
  }
}
